package io.mvvm.security.config;

import io.mvvm.security.filter.JwtAuthenticationTokenFilter;
import io.mvvm.security.filter.AjaxUsernamePasswordAuthenticationFilter;
import io.mvvm.security.handler.*;
import io.mvvm.security.handler.UserDetailsHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsHandler selfUserDetailsService;
    @Resource
    private AuthenticationEntryPointImpl authenticationEntryPoint;
    @Resource
    private AuthenticationSuccessHandlerImpl authenticationSuccessHandler;
    @Resource
    private AuthenticationFailureHandlerImpl authenticationFailureHandler;
    @Resource
    private LogoutSuccessHandlerImpl logoutSuccessHandler;
    @Resource
    private AccessDeniedHandlerImpl accessDeniedHandler;
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
//    @Resource
//    private UrlAuthenticationFilter urlAuthenticationFilter;

    @Resource
    private UrlRolesFilterHandler urlRolesFilterHandler;
    @Resource
    private UrlRoleAuthHandler urlRoleAuthHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        // 加入自定义的安全认证，
        auth.userDetailsService(selfUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /* 开启跨域共享，  跨域伪造请求限制=无效 */
        http.cors().and().csrf().disable(); // 无状态

        /* rest 无状态 无session */
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        /* 无需认证的请求路径 */
        http.authorizeRequests().antMatchers("/forciblyLogin").permitAll();
        /* 动态url权限 */
        http.authorizeRequests().withObjectPostProcessor(new DefinedObjectPostProcessor());
        /* url决策 */
        http.authorizeRequests().accessDecisionManager(accessDecisionManager());
        /* 开启授权认证 */
        http.authorizeRequests().anyRequest().authenticated();
        /* RBAC 动态 url 认证 */
//        http.authorizeRequests().anyRequest().access("@rbacAuthorityService.hasPermission(request,authentication)");

        /* 表单登录配置 */
        http.formLogin().usernameParameter("username").passwordParameter("password").loginProcessingUrl("/doLogin").permitAll();
        // 登录成功后的处理
        http.formLogin().successHandler(authenticationSuccessHandler);
        // 登录失败后的处理
        http.formLogin().failureHandler(authenticationFailureHandler);

        /* 登录过期/未登录 处理 */
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
        /* 权限不足(没有赋予角色) 处理 */
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);

        /* 自定义登陆表单解析器 */
        http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        /* 配置token验证过滤器 */
        http.addFilterBefore(jwtAuthenticationTokenFilter, AjaxUsernamePasswordAuthenticationFilter.class);
        /* 路由权限验证过滤器 */
//        http.addFilterAfter(urlAuthenticationFilter, JwtAuthenticationTokenFilter.class);

        /* 登出 */
        http.logout().logoutSuccessHandler(logoutSuccessHandler);
    }

    /**
     * 自定义JSON登陆参数解析
     * @return
     * @throws Exception
     */
    @Bean
    public AjaxUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
        AjaxUsernamePasswordAuthenticationFilter filter = new AjaxUsernamePasswordAuthenticationFilter();
        filter.setAuthenticationManager(super.authenticationManagerBean());
        filter.setFilterProcessesUrl("/jsonLogin");
        // 登陆成功处理器
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        // 登陆失败处理器
        filter.setAuthenticationFailureHandler(authenticationFailureHandler);
        return filter;
    }

    /**
     * 配置跨域
     * @return
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }

    /**
     * AffirmativeBased – 任何一个AccessDecisionVoter返回同意则允许访问
     * ConsensusBased – 同意投票多于拒绝投票（忽略弃权回答）则允许访问
     * UnanimousBased – 每个投票者选择弃权或同意则允许访问
     *
     * 决策管理
     */
    private AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
        decisionVoters.add(new WebExpressionVoter());
        decisionVoters.add(new AuthenticatedVoter());
        decisionVoters.add(new RoleVoter());
        /* 路由权限管理 */
        decisionVoters.add(urlRoleAuthHandler);
        return new UnanimousBased(decisionVoters);
    }

    class DefinedObjectPostProcessor implements ObjectPostProcessor<FilterSecurityInterceptor> {
        @Override
        public <O extends FilterSecurityInterceptor> O postProcess(O object) {
            object.setSecurityMetadataSource(urlRolesFilterHandler);
            return object;
        }
    }
}